package object

import (
	"bytes"
	"fmt"
	"hash/fnv"
	"monkey/ast"
	"strings"
)

type BuiltinFunction func(args ...Object) Object

// 对象类型
type ObjectType string

const (
	// NULL_OBj null
	NULL_OBj = "NULL"
	// ERROR_OBJ error
	ERROR_OBJ = "ERROR"

	// INTEGER_OBJ int
	INTEGER_OBJ = "INTEGER"
	// BOOLEAN_OBJ bool
	BOOLEAN_OBJ = "BOOLEAN"
	// STRING_OBJ string
	STRING_OBJ = "STRING"

	// RETURN_VALUE_OBJ return
	RETURN_VALUE_OBJ = "RETURN_VALUE"

	// FUNCTION_OBJ fn
	FUNCTION_OBJ = "FUNCTION"
	BUILTIN_OBJ  = "BUILTIN"

	// ARRAY_OBJ array
	ARRAY_OBJ = "ARRAY"
	// HASH_OBJ hash
	HASH_OBJ = "HASH"
)

type HashKey struct {
	Type  ObjectType
	Value uint64
}

type Hashable interface {
	HashKey() HashKey
}

// Object 对象接口
type Object interface {
	// Type 对象类型
	Type() ObjectType
	// Inspect 对象的值的字符串形式
	Inspect() string
}

// Integer int对象
type Integer struct {
	Value int64
}

// Type int类型
func (i *Integer) Type() ObjectType {
	return INTEGER_OBJ
}

// Inspect int类型的字符串形式的值
func (i *Integer) Inspect() string {
	return fmt.Sprintf("%d", i.Value)
}

func (i *Integer) HashKey() HashKey {
	return HashKey{Type: i.Type(), Value: uint64(i.Value)}
}

// Boolean bool
type Boolean struct {
	Value bool
}

func (b *Boolean) Type() ObjectType {
	return BOOLEAN_OBJ
}

func (b *Boolean) Inspect() string {
	return fmt.Sprintf("%t", b.Value)
}

func (b *Boolean) HashKey() HashKey {
	var value uint64

	if b.Value {
		value = 1
	} else {
		value = 0
	}

	return HashKey{Type: b.Type(), Value: value}
}

// Null null类型
// 注意：这是一个空结构体，是go特有的语法
type Null struct {
}

func (n *Null) Type() ObjectType {
	return NULL_OBj
}

func (n *Null) Inspect() string {
	return "null"
}

// ReturnValue return的值类型
type ReturnValue struct {
	Value Object
}

func (rv *ReturnValue) Type() ObjectType {
	return RETURN_VALUE_OBJ
}

func (rv *ReturnValue) Inspect() string {
	return rv.Value.Inspect()
}

// Error error类型
type Error struct {
	Message string
}

func (e *Error) Type() ObjectType {
	return ERROR_OBJ
}

func (e *Error) Inspect() string {
	return "Error:" + e.Message
}

// Function fn类型
// 注意：Env是环境，保存了函数外部和内部的环境，而环境可以理解成变量的集合
type Function struct {
	Parameters []*ast.Identifier
	Body       *ast.BlockStatement
	Env        *Environment
}

func (f *Function) Type() ObjectType {
	return FUNCTION_OBJ
}

func (f *Function) Inspect() string {
	var out bytes.Buffer

	var params []string
	for _, p := range f.Parameters {
		params = append(params, p.String())
	}

	out.WriteString("fn")
	out.WriteString("(")
	if len(params) > 0 {
		out.WriteString(strings.Join(params, ","))
	}
	out.WriteString(")")
	out.WriteString(") {\n")
	out.WriteString(f.Body.String())
	out.WriteString("}\n")

	return out.String()
}

// String 字符串
type String struct {
	Value string
}

func (s *String) Type() ObjectType {
	return STRING_OBJ
}

func (s *String) Inspect() string {
	return s.Value
}

func (s *String) HashKey() HashKey {
	h := fnv.New64a()
	h.Write([]byte(s.Value))

	return HashKey{Type: s.Type(), Value: h.Sum64()}
}

type Builtin struct {
	Fn BuiltinFunction
}

func (b *Builtin) Type() ObjectType {
	return BUILTIN_OBJ
}

func (b *Builtin) Inspect() string {
	return "builtin function"
}

// Array 数组
type Array struct {
	Elements []Object
}

func (ao *Array) Type() ObjectType {
	return ARRAY_OBJ
}

func (ao *Array) Inspect() string {
	var out bytes.Buffer

	elements := []string{}
	for _, e := range ao.Elements {
		elements = append(elements, e.Inspect())
	}

	out.WriteString("[")
	out.WriteString(strings.Join(elements, ", "))
	out.WriteString("]")

	return out.String()
}

type HashPair struct {
	Key   Object
	Value Object
}

type Hash struct {
	Pairs map[HashKey]HashPair
}

func (h *Hash) Type() ObjectType { return HASH_OBJ }
func (h *Hash) Inspect() string {
	var out bytes.Buffer

	pairs := []string{}
	for _, pair := range h.Pairs {
		pairs = append(pairs, fmt.Sprintf("%s: %s",
			pair.Key.Inspect(), pair.Value.Inspect()))
	}

	out.WriteString("{")
	out.WriteString(strings.Join(pairs, ", "))
	out.WriteString("}")

	return out.String()
}
